/*****************************************************************************************/
/* ECSE 426 - Microprocessor - Fall 2009
/* Lab Project - Includes control for PS/2 (Keyboard)
/* KEYBOARD.C
/* @author: Danny Wu - 260238209 - B.Comp.Eng
/* @author: Han-Sheng Chin - 260209715 - B.Elec.Eng
/* @author: Benoit Gravel - 260230976 - B.Comp.Eng
/* @author: Bradley Thompson - 2602XXXXX - B.Comp.Eng

/* @version: 11.11.1.48
/****************************************************************************************/

#include <keyboard.h>
//#include <constants.h>
//control for debug mode
#define DEBUG_MODE FALSE
//general constants

// Global variables from main.c
extern int KeyboardState;
extern int KeyboardReceiveBit;
extern char KeyboardDataBuffer[8];

//Local variables
char BreakSeq=0;

void initKeyboard (void){
  //initialize keyboard (P1.0 -->CLOCK line, P1.1-->DATA line)
  P1DIR &= 0xFC;                // input (P1.0,1.1)
  P1SEL &= 0xFC;                // I/O for P1.0,1.1
  P1IES = 0x01;                 //Clock interupt on high to low transitions
  P1IE = 0x01;                  //Enable interupts on clock line
  P1IFG = 0x00;                 // Clear interrupt flag
  return ;
}//end initKeyboard

void hostSend (char data[8]){
//TODO: //add timer for time a and B and abort
  int i;
  int parity=0;
  
  KeyboardReceiveBit = 0; //cancel current transfer if there were any
  KeyboardState = 1;  //go into HostSendState

  P1IE = 0x00;    //Disable interupts to avoid conflicts
  P1DIR |= 0x01;      //take clock control (output)
  P1OUT &= 0xFE;      //pull clock low
  wait(100);          //wait 100ms
  P1DIR |= 0x02;      //Data output
  P1OUT &= 0xFD;      //pull DATA low
  P1DIR &= 0xFE;      //release clock (input)
  while(ClockLine() != 0);  //wait for device to bring clock low
  for(i=0;i<8;i++){  //loop through 8 data bits
    if(data[i]){
      P1OUT |= 0x02;  //set data bit
      parity ++;
    }
    else
      P1OUT &= 0xFD;  //reset data bit
    while(ClockLine() != 1);  //wait for clock to go high
    while(ClockLine() != 0);  //wait for device to bring clock low
  }//end sending data bits
  //parity bit
  if((parity%2)==1)//if odd number of 1's
    P1OUT &= 0xFD;  //reset data bit
  else  //even number of 1's
    P1OUT |= 0x02;  //set data bit
  while(ClockLine() != 1);  //wait for clock to go high
  while(ClockLine() != 0);  //wait for device to bring clock low
  //end sending parity bit
  P1DIR &= 0xFD;    //release DATA line (input)
  while(DataLine() != 0); //wait for device to bring DATA low    //ACK form Device
  while(ClockLine() != 0);  //wait for device to bring clock low  //One last clock pulse
  while(DataLine() != 1); //wait for device to release DATA (high)
  while(ClockLine() != 1);  //wait for device to release clock (high)
  
  KeyboardState = 0;  //go back into iddle state
  initKeyboard();   //Go back in wait mode

}//end hostSend


int HostReceive (void) __interrupt[PORT1_VECTOR]{
  int i;
  int parity;

  switch(KeyboardState){

      case 0: //idle
        if ((DataLine()== 0) ){ //Start Bit
          KeyboardState = 2; //go into HostReceive mode
          KeyboardReceiveBit = 1; //Start Bit received
        }
        break;
      
      case 1: //HostSend
        //should never reach here since interupts disabled in HOST SEND
        break;
      
      case 2; //HostReceive (DeviceSend)
        if (KeyboardReceiveBit<= 9){  //if Data bits
          KeyboardDataBuffer[KeyboardReceiveBit-1] = DataLine();
          KeyboardReceiveBit++;
        }
        else if (KeyboardReceiveBit == 10){ //parity bit
          parity=0;
          for(i=0;i<8;i++){
            if (KeyboardDataBuffer[i] == '1')
              parity++;
          )
          parity = parity%2;
          if( DataLine() != !parity){//if expected parity error
            hostSend(_Resend);
            break;
          }
          else
            KeyboardReceiveBit++;
        }
        else{ //Stop Bit
          if(DataLine()==1){ // if Stop bit on DATA line
            KeyboardReceiveBit = 0;
            KeyboardState = 0;  //go back into iddle state
            ParseCode(KeyboardDataBuffer);
          }
        }
        break;      
  }//end switch  

}//end HostReceive interrupt

//Parses content of KeyboardDataBuffer
void ParseCode(int KeyboardDataBuffer){
  char key = lookupKey(KeyboardDataBuffer);  
  if (BreakSeq == '1'){//this key was released
    BreakSeq = 0;    
  }
  else{//this key was pressed
    if (KeyboardDataBuffer == 0xF0){//Break code
      BreakSeq = 1; //signal BreakCode for next key
      break;
    }
    if (key != 'NUL'){
      //TODO print key;
    }
  }
}




//Helper function returns DataLine input
int DataLine(void){
  if((P1IN & 0x02)== 0x00)
    return 0;
  else
    return 1;
}
//Helper function returns ClockLine input
int ClockLine(void){
  if((P1IN & 0x01)== 0x00)
    return 0;
  else
    return 1;
}

//Lookup table for Keyboard ScanCode --> ACSII char
char lookupKey(int KeyboardDataBuffer){
  switch(KeyboardDataBuffer){
    case(0x0E): return '~';
    case(0x16): return '1';
    case(0x1E): return '2';
    case(0x26): return '3';
    case(0x25): return '4';
    case(0x2E): return '5';
    case(0x36): return '6';
    case(0x3D): return '7';
    case(0x3E): return '8';
    case(0x46): return '9';
    case(0x45): return '0';
    case(0x4E): return '-';
    case(0x15): return 'Q';
    case(0x1D): return 'W';
    case(0x24): return 'E';
    case(0x2D): return 'R';
    case(0x2C): return 'T';
    case(0x35): return 'Y';
    case(0x3C): return 'U';
    case(0x43): return 'I';
    case(0x44): return 'O';
    case(0x4D): return 'P';
    case(0x54): return '[';
    case(0x5B): return ']';
    case(0x1C): return 'A';
    case(0x1B): return 'S';
    case(0x23): return 'D';
    case(0x2B): return 'F';
    case(0x34): return 'G';
    case(0x33): return 'H';
    case(0x3B): return 'J';
    case(0x42): return 'K';
    case(0x4B): return 'L';
    case(0x1A): return 'Z';
    case(0x22): return 'X';
    case(0x21): return 'C';
    case(0x2A): return 'V';
    case(0x32): return 'B';
    case(0x31): return 'N';
    case(0x3A): return 'M';
    case(0x41): return ',';
    case(0x49): return '.';
    case(0x4A): return '/';
    case(0x29): return 'Space';//SPACE
    //added
    case(0x5A): return 'LF'; //ENTER
    case(0x76): return 'ESC'; //ESCAPE
    case(0x66): return 'BS'; //Backspace
    default: return  'NUL'; //NULL
  }//end switch
}